/**
 * OSAmI: Open Source Ambient Intelligence
 * A simple publish/subscribe/dispatch API (based on multicast communications) for constrained devices.
 * Copyright (C) Lionel Debroux 2006 (as a TER/Magistere M1 student in the LSR-IMAG-ADELE team, for a demo in the H-Omega project)
 * Copyright (C) Lionel Debroux 2010 (as an engineer in the LCIS-INPG-CTSYS team, for a pre-demo in the FR part of the OSAMI project)
 * Contact: osami-general@osami-commons.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
 */

package fr.imag.adele.homega.impl.unicast;

import java.util.logging.Logger;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.DatagramSocket;

/**
 * This class implements a sender device: it binds to a given address and port, and sends messages on the given topic and port.
 */
public class UnicastSenderImpl implements UnicastSender {

	private InetAddress selfaddr;
	private String selfaddrstr;
	private int selfport;
	private DatagramSocket socket;
	private DatagramPacket p;
	private String name;
	private boolean hasQuit = true;

	private static final int MAX_PACKET_LENGTH = 1400; // Smaller than the largest possible "UDP packet" (to leave room for layer 3, 2 data).

	public UnicastSenderImpl(String name) {
		this.selfaddr = null;
		this.selfaddrstr = null;
		this.selfport = 0;
		this.socket = null;
		this.name = name;
		hasQuit = false;
	}

	public synchronized void bind(String _selfaddr, int _selfport) throws IOException {
		InetAddress ia;

		ia = InetAddress.getByName(_selfaddr);

		this.selfaddr = ia;
		this.selfaddrstr = _selfaddr;
		this.selfport = _selfport;

		initSocket(_selfaddr,_selfport);
	}

	public synchronized void send(String topic, int port, String message) throws IOException {
		InetAddress ia;
		OutboundMessage out;
		byte[] buf;
		byte[] buf2;
		int maxlen;

		// String -> InetAddress.
		ia = InetAddress.getByName(topic);

		//Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info("In topic \"" + topic + "\", " + name + " sending message:\n\"" + message +"\"");

		// Construct and send packet.
		out = new OutboundMessage(selfaddrstr,topic,port,message);

		Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info("Port: " + port);
		Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info(out.toString());
		buf2 = out.toString().getBytes();
		maxlen = Math.min(MAX_PACKET_LENGTH, buf2.length);
		buf = new byte[maxlen+1];

		System.arraycopy(buf2, 0, buf, 0, maxlen);
		buf[maxlen] = 0;

		this.p = new DatagramPacket(buf, buf.length-1);
		p.setAddress(ia);
		p.setPort(port);
		p.setLength(maxlen+1); // Terminating NULL character.

		/*System.out.println("SENDING DATA:");
		for (int i = 0; i < maxlen+1; i++) {
			if (i % 16 == 0) {
				System.out.println();
			}
			System.out.printf("%02X ",(p.getData())[i]);
		}
		System.out.printf("\nLength: %04X\n",(buf2.length));
		System.out.println();*/
		

		socket.send(p);
	}
	
	// Code common to multiple methods...
	private synchronized void initSocket(String _selfaddr, int port) throws IOException {
		InetSocketAddress isa;
		InetAddress ia;

		if (socket != null) {
			socket.close();
		}

		try {
			ia = InetAddress.getByName(_selfaddr);
			
			isa = new InetSocketAddress(ia,port);

			socket = new DatagramSocket(port);

			//Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info("Sender: multicast socket " + ia + ":" + port + " created, " + "joined group " + isa + " !");

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public synchronized void shutdown() {
		if ((socket != null) && (!(socket.isClosed()))) {
			socket.close();
		}
		hasQuit = true;
	}

	public String toString() {
		return name;
	}

	/**
	 * Bug catcher...
	 */
	@Override
	public void finalize() {
		if (!hasQuit) {
			System.out.println("\u001B[1;5;31mForgot to quit " + super.toString() + " !\u001B[m");
			try {
				shutdown();
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	// Getters, setters.
	/*public synchronized int getSelfport() {
		return selfport;
	}

	public synchronized void setSelfport(int selfport) throws IOException {
		try {
			initSocket(selfaddr.toString(),selfport);
			this.selfport = selfport;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public synchronized String getSelfaddr() {
		return selfaddr.toString();
	}

	public synchronized void setSelfaddr(String selfaddr) {
		try {
			initSocket(selfaddr,selfport);
			this.selfaddr = InetAddress.getByName(selfaddr);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}*/

}
